Chapter 6: External Automation
Internal scripts in Enterprise Architect (EA) are perfect for quick utilities, governance checks, and tidy-ups that live with the repository. Sometimes you need more. Integrations with registries and backlog tools, JSON-heavy exports, analytics over thousands of elements, CI/CD checks, and richer UX all benefit from external automation.
External automation means driving EA from a separate program (e.g., C#/.NET, Python) via EA’s COM interface. Your program attaches to (or launches) EA, grabs the same Repository object you use inside scripts, and then performs work — with modern language ecosystems, logging, tests, packaging, and debugging.
Why external automation?
Modern capabilities: async, structured logging, JSON, LINQ/pandas.
Ecosystem: proper source control, CI, test frameworks, packaging.
Integration: REST/Graph APIs, registries, issue trackers, data platforms.
Maintainability: clearer separation of concerns and deployable tools.
Scale: process large repositories, batch operations, pipelines.
How it works (at a glance)
EA registers a COM server (EA.App). Your tool attaches (or launches), obtains EA.App.Repository, and then uses the familiar API for packages, elements, connectors, diagrams, SQL reads, and so on — just from outside EA.
Example 6.1 — End-to-End External Automation with EAConnect
The example below shows a practical, fully-commented C# console utility that:
Connects to EA (attach or launch)
Ensures a model is open (optionally open a specified file)
Finds a working package (selected in UI or falls back to first root)
Creates a new Element in that package
Persists and refreshes the UI so the change is immediately visible
The EAConnect helper (namespace EA.Automation) centralises COM wiring, timing/polling, and small UX conveniences. The full helper implementation and setup guidance live in Appendix A – EAConnect Helper & Setup.
Example 6.1 - EA Automation – Example Utility
// =============================================================================================
// Example 6.1
// File: Program.cs
// Project: EA Automation – Example Utility
// Author: <Your Name>
//
// Created: 2025-08-31
// Last Update: 2025-08-31
//
// PURPOSE
// -------
// Demonstrates a practical end-to-end EA automation task using the EAConnect helper:
// 1) Connect to Enterprise Architect (attach or launch)
// 2) Ensure a model is open (optionally open a specified .qea/.qeax/.eapx)
// 3) Identify a target package (selected in the UI or fallback to the first root)
// 4) Create a new Element in that package
// 5) Persist and refresh the UI so the user immediately sees the change
//
// USAGE
// -----
// From a developer console (matching EA bitness):
// MyEaTool.exe "New Class Name" "Class" "Optional notes"
// "OptionalProfile::OptionalStereotype"
//
// Arguments (all optional; sensible defaults provided):
// [0] name : Element name (default: "New Class")
// [1] type : Element type (default: "Class"; e.g., "Component", "Requirement", "UseCase")
// [2] notes: Notes/description (default: "Created by example utility.")
// [3] stereotypeEx : Fully qualified stereotype (default: none; e.g., "BPMN2.0::Task")
//
// PRE-REQUISITES
// --------------
// - Windows + .NET 8 (Windows)
// - <TargetFramework>net8.0-windows</TargetFramework>
// - <PlatformTarget>x64</PlatformTarget> (for EA 64-bit) or x86 for 32-bit EA
// - Reference Interop.EA.dll from your EA installation
// - Entry point marked [STAThread]
//
// NOTES
// -----
// If nothing is selected in EA’s Project Browser, we default to the first root package.
// After creating the element, call element.Update() to persist and then refresh the model view.
// We also advise EA’s UI that the element changed to update open diagrams.
//
// UPDATE HISTORY
// --------------
// - 2025-08-31: Initial illustrative example.
// ==============================================================================================
#nullable enable
using System;
using EA;
using EA.Automation; // EAConnect helper
internal static class Program
{
[STAThread] // EA COM automation expects an STA thread
private static void Main(string[] args)
{
// --------------------------------------------
// 0) Parse command-line args with safe defaults
// --------------------------------------------
string name = args.Length > 0 ? args[0] : "New Class";
string type = args.Length > 1 ? args[1] : "Class";
string notes = args.Length > 2 ? args[2] : "Created by example utility.";
string? stereotype = args.Length > 3 ? args[3] : null;
// Optional: set this if you want to force-opening a specific model file when none is open.
// If you’re writing a general utility, you might leave it null and let the user open/select.
string? modelPath = null;
// Example:
// modelPath = @"C:\Users\Public\Documents\Sparx Systems\EA\EA Example.qea";
Console.WriteLine("EA Automation Example – Add Element to Package");
Console.WriteLine($"Requested element: Name='{name}', Type='{type}', StereotypeEx='{stereotype ?? "(none)"}'");
try
{
// ------------------------------------------------------------
// 1) Connect to EA, ensure a model is open, and show the UI
// ------------------------------------------------------------
using var ea = EAConnect.AttachOrLaunch(new EAConnect.Options
{
PreferAttach = true, // attach to a running EA if possible
LaunchIfNotRunning = true, // otherwise launch a new EA instance
ModelPath = modelPath, // open a known model if none is open
ShowUI = true, // show EA UI so user sees what happens
StartupWaitMs = 1500, // small settle time after opening/launching
RepoPollMs = 100, // poll interval for App.Repository
RepoPollMax = 50 // up to 5 seconds for Repo to appear
});
// -----------------------------------------------------------------------
// 2) Find the working package: selected package or fallback to root[0]
// -----------------------------------------------------------------------
Package pkg = ea.GetSelectedPackageOrRoot();
Console.WriteLine($"Target package: {pkg.Name} (ID={pkg.PackageID})");
// --------------------------------------------------------
// 3) Create a new element inside that package (core steps)
// - AddNew(name, type)
// - Set optional properties (Notes, StereotypeEx)
// - Update() to persist changes
// --------------------------------------------------------
Element el = (Element)pkg.Elements.AddNew(name, type);
if (!string.IsNullOrEmpty(stereotype))
{
// For MDG types, you can set fully qualified stereotype (e.g., "BPMN2.0::Task")
el.StereotypeEx = stereotype;
}
if (!string.IsNullOrWhiteSpace(notes))
el.Notes = notes;
// Persist the new element into the repository
if (!el.Update())
{
// If Update() returns false, EA has an error you can read via
// repo.GetLastError()
throw new InvalidOperationException("Element.Update() failed.");
}
// ------------------------------------------------------------------------
// 4) Refresh EA’s UI so the user immediately sees the new element appear
// ------------------------------------------------------------------------
// - AdviseElementChange: tells EA something changed – updates any open diagrams
// - RefreshModelView : reloads the package in the Project Browser
ea.Repo.AdviseElementChange(el.ElementID);
ea.Repo.RefreshModelView(pkg.PackageID);
Console.WriteLine($"Created: {el.Name} [{el.Type}]");
Console.WriteLine($"GUID : {el.ElementGUID}");
// -----------------------------------------
// 5) Bonus: enumerate elements in the pkg
// -----------------------------------------
Console.WriteLine();
Console.WriteLine("Elements now present in package:");
short count = pkg.Elements.Count;
for (short i = 0; i < count; i++)
{
var existing = (Element)pkg.Elements.GetAt(i);
Console.WriteLine($" - {existing.Name} ({existing.Type})");
}
Console.WriteLine();
Console.WriteLine("Done. Press any key to exit.");
Console.ReadKey();
}
catch (Exception ex)
{
// In production tools you might log the stack. For a book example, keep output readable.
Console.WriteLine();
Console.WriteLine("ERROR:");
Console.WriteLine(ex.Message);
Console.WriteLine("Tip: Ensure EA is installed (matching x64/x86), Interop.EA.dll is referenced,");
Console.WriteLine(" and that a model is open or ModelPath is set.");
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}Trade-offs
Pros
Modern language, logging, testing, and packaging.
Clear separation from the model repository.
Works well for integrations, analytics, and pipelines.
Easier to scale and maintain over time.
Cons
Requires setup (Interop reference, bitness alignment, COM availability).
Windows/COM dependency.
Distribution/versioning outside the model.
Speed + power mean mistakes propagate quickly if you skip safety.
Safety still matters
Prefer dry-run patterns and audit logs for batch updates.
Use SQL-find + API-write for scale (helper provides this).
Test on sandbox repositories first.
Batch writes and refresh the UI once per operation.
Where this goes next
Most teams adopt a blended approach: internal scripts for quick hygiene; external utilities for integrations and heavy lifting; add-ins for stable, menu-driven capabilities. With EAConnect, you keep examples concise while centralising boilerplate and sharp edges in one place.
See Appendix E – External Automation Setup for the full helper class (verbose header, usage, assumptions, dependencies, and update history), plus environment notes (Interop.EA.dll, bitness, COM), optional “directory-only” output pickers, CSV logging utilities, a curate-then-apply pipeline, and the SQL-accelerated find / API-safe write pattern.